<!DOCTYPE html>
<title>@scroll-timeline: Element-based offsets</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#element-based-offset-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
  #scroller {
    overflow: scroll;
    width: 100px;
    height: 100px;
  }
  .filler {
    height: 150px;
    background-color: darkgray;
  }
  .offset {
    height: 50px;
    background-color: green;
  }
  @keyframes expand {
    from { width: 100px; }
    to { width: 200px; }
  }
  @scroll-timeline timeline_start_start {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#offset1);
    end: selector(#offset2);
  }
  @scroll-timeline timeline_end_end {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#offset1) end;
    end: selector(#offset2) end;
  }
  @scroll-timeline timeline_end_start {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#offset1) end;
    end: selector(#offset2) start;
  }
  @scroll-timeline timeline_end_1_end {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#offset1) end 1;
    end: selector(#offset2) end;
  }
  @scroll-timeline timeline_end_start_05 {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#offset1) end;
    end: selector(#offset2) 0.5;
  }
  @scroll-timeline timeline_start_400px {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#offset1);
    end: 400px;
  }
  @scroll-timeline timeline_50px_end {
    source: selector(#scroller);
    time-range: 10s;
    start: 50px;
    end: selector(#offset2) end;
  }
  @scroll-timeline timeline_outside {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#offset_outside);
    end: auto;
  }
  @scroll-timeline timeline_display_none {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#offset_display_none);
    end: auto;
  }
  @scroll-timeline timeline_null_target {
    source: selector(#scroller);
    time-range: 10s;
    start: selector(#no_such_id);
    end: selector(#no_such_id);
  }

  #container > div {
    width: 0px;
    animation-name: expand;
    animation-duration: 10s;
    animation-timing-function: linear;
  }
  /* Ensure stable expectations if feature is not supported */
  @supports not (animation-timeline:foo) {
    #container > div { animation-play-state: paused; }
  }
  #element_start_start { animation-timeline: timeline_start_start; }
  #element_end_end { animation-timeline: timeline_end_end; }
  #element_end_start { animation-timeline: timeline_end_start; }
  #element_end_1_end { animation-timeline: timeline_end_1_end; }
  #element_end_start_05 { animation-timeline: timeline_end_start_05; }
  #element_start_400px { animation-timeline: timeline_start_400px; }
  #element_50px_end { animation-timeline: timeline_50px_end; }
  #element_outside { animation-timeline: timeline_outside; }
  #element_display_none { animation-timeline: timeline_display_none; }
  #element_null_target { animation-timeline: timeline_null_target; }
</style>
<div id=scroller>
  <div id=contents>
    <div class=filler></div>
    <div class=offset id=offset1></div>
    <div class=filler></div>
    <div class=offset id=offset2></div>
    <div class=filler></div>
  </div>
</div>
<div id=offset_outside></div>
<div id=offset_display_none style="display:none"></div>
<div id=container>
  <div id=element_start_start></div>
  <div id=element_end_end></div>
  <div id=element_end_start></div>
  <div id=element_end_1_end></div>
  <div id=element_end_start_05></div>
  <div id=element_start_400px></div>
  <div id=element_50px_end></div>
  <div id=element_outside></div>
  <div id=element_display_none></div>
  <div id=element_null_target></div>
</div>
<script>

  // The contents of the scroller looks approximately like this:
  //
  //  +-------+
  //  |       |
  //  | 150px | filler
  //  |       |
  //  +-------+
  //  +-------+
  //  | 50px  | #offset1
  //  +-------+
  //  +-------+
  //  |       |
  //  | 150px | filler
  //  |       |
  //  +-------+
  //  +-------+
  //  | 50px  | #offset2
  //  +-------+
  //  +-------+
  //  |       |
  //  | 150px | filler
  //  |       |
  //  +-------+
  //
  // The height of the scrollport is 100px.

  // Scrolls top to 'offset', waits for a frame, then call the provided
  // assertions function.
  function test_scroll(element, offset, assertions, description) {
    promise_test(async (t) => {
      scroller.scrollTop = offset;
      await waitForNextFrame();
      assertions();
    }, `${description} [${element.id}]`);
  }

  // Tests that the computed value of 'width' on element is the expected value
  // after scrolling top to the specifed offset.
  function test_width_at_scroll_top(element, offset, expected) {
    test_scroll(element, offset, () => {
      assert_equals(getComputedStyle(element).width, expected);
    }, `Scroll at offset ${offset} updates animation correctly`);
  }

  // [200, 400]
  test_width_at_scroll_top(element_start_start, 0, '0px');
  test_width_at_scroll_top(element_start_start, 199, '0px');
  test_width_at_scroll_top(element_start_start, 200, '100px');
  test_width_at_scroll_top(element_start_start, 300, '150px');
  test_width_at_scroll_top(element_start_start, 398, '199px');
  test_width_at_scroll_top(element_start_start, 400, '0px');

  // [50, 250]
  test_width_at_scroll_top(element_end_end, 0, '0px');
  test_width_at_scroll_top(element_end_end, 49, '0px');
  test_width_at_scroll_top(element_end_end, 50, '100px');
  test_width_at_scroll_top(element_end_end, 150, '150px');
  test_width_at_scroll_top(element_end_end, 248, '199px');
  test_width_at_scroll_top(element_end_end, 250, '0px');

  // [50, 400]
  test_width_at_scroll_top(element_end_start, 0, '0px');
  test_width_at_scroll_top(element_end_start, 49, '0px');
  test_width_at_scroll_top(element_end_start, 50, '100px');
  test_width_at_scroll_top(element_end_start, 225, '150px');
  test_width_at_scroll_top(element_end_start, 393, '198px');
  test_width_at_scroll_top(element_end_start, 400, '0px');

  // [100, 250]
  test_width_at_scroll_top(element_end_1_end, 0, '0px');
  test_width_at_scroll_top(element_end_1_end, 99, '0px');
  test_width_at_scroll_top(element_end_1_end, 100, '100px');
  test_width_at_scroll_top(element_end_1_end, 175, '150px');
  test_width_at_scroll_top(element_end_1_end, 247, '198px');
  test_width_at_scroll_top(element_end_1_end, 250, '0px');

  // [50, 375]
  test_width_at_scroll_top(element_end_start_05, 0, '0px');
  test_width_at_scroll_top(element_end_start_05, 49, '0px');
  test_width_at_scroll_top(element_end_start_05, 50, '100px');
  test_width_at_scroll_top(element_end_start_05, 206, '148px');
  test_width_at_scroll_top(element_end_start_05, 362, '196px');
  test_width_at_scroll_top(element_end_start_05, 375, '0px');

  // [200, 300]
  test_width_at_scroll_top(element_start_400px, 0, '0px');
  test_width_at_scroll_top(element_start_400px, 199, '0px');
  test_width_at_scroll_top(element_start_400px, 200, '100px');
  test_width_at_scroll_top(element_start_400px, 300, '150px');
  test_width_at_scroll_top(element_start_400px, 398, '199px');
  test_width_at_scroll_top(element_start_400px, 400, '0px');

  // [50, 250]
  test_width_at_scroll_top(element_50px_end, 0, '0px');
  test_width_at_scroll_top(element_50px_end, 49, '0px');
  test_width_at_scroll_top(element_50px_end, 50, '100px');
  test_width_at_scroll_top(element_50px_end, 150, '150px');
  test_width_at_scroll_top(element_50px_end, 248, '199px');
  test_width_at_scroll_top(element_50px_end, 250, '0px');

  // Offset not a decendant of scroller (=> no effect value)
  test_width_at_scroll_top(element_outside, 0, '0px');
  test_width_at_scroll_top(element_outside, 100, '0px');
  test_width_at_scroll_top(element_outside, 200, '0px');
  test_width_at_scroll_top(element_outside, 300, '0px');
  test_width_at_scroll_top(element_outside, 400, '0px');
  test_width_at_scroll_top(element_outside, 450, '0px');

  // Target of element-based offset has no layout box (=> no effect value)
  test_width_at_scroll_top(element_display_none, 0, '0px');
  test_width_at_scroll_top(element_display_none, 100, '0px');
  test_width_at_scroll_top(element_display_none, 200, '0px');
  test_width_at_scroll_top(element_display_none, 300, '0px');
  test_width_at_scroll_top(element_display_none, 400, '0px');
  test_width_at_scroll_top(element_display_none, 450, '0px');

  // Target of element-based offset is null (=> no effect value)
  test_width_at_scroll_top(element_null_target, 0, '0px');
  test_width_at_scroll_top(element_null_target, 100, '0px');
  test_width_at_scroll_top(element_null_target, 200, '0px');
  test_width_at_scroll_top(element_null_target, 300, '0px');
  test_width_at_scroll_top(element_null_target, 400, '0px');
  test_width_at_scroll_top(element_null_target, 450, '0px');

</script>
